React メモ化(React.memo, useMemo, useCallBack)
#React #性能
指針
hr.icon
重要.icon React.memo、useMemo、useCallbackは、無駄な再レンダリングを防ぐために使う
親コンポーネントが再レンダリングする際、子コンポーネントも一緒にレンダリングされる可能性がある。
ただ、子コンポーネントの表示内容は変わらないのに、レンダリングされるってのは無駄である。
この無駄なレンダリングによって、処理が重くなる可能性あり。
ということで、無駄レンダリングをさせないようメモ化するってわけ。
以下のような使い方がいいんだと。useEffectとかを無駄に発火させないような使い方が。
code: sample.js
function Foo({bar, baz}) {
React.useEffect(() => {
const options = {bar, baz}
buzz(options)
}, bar, baz)
return <div>foobar</div>
}
function Blub() {
const bar = React.useCallback(() => {}, [])
const baz = React.useMemo(() => 1, 2, 3, [])
return <Foo bar={bar} baz={baz} />
}
これ、Blubでメモ化してるのだが、メモ化してないと、Blubのレンダリングの度に別の値として扱われるようになる。
それによってFooのuseEffectが、親コンポーネントのレンダリングの度に動くようになってしまう。
重要.icon ただし、メモ化は計測しながら慎重に行う必要がある
メモ化にもコストはあるらしく、無思考にメモ化を乱用すると、再レンダリングのコストよりも大きかったなんてことが起きうる。
無闇に使わないほうがいい。というか再レンダリングが性能のボトルネックになってるとわかるまで使う必要はないかも。
stateが複数関わるような大きなコンポーネントは作らないほうがいい
これはあまりメモ化と関係ないけど、指針として大事かも。
対象コンポーネントは、そのstateと関係ないのに、そのstateが更新されたら一緒に再レンダリングされるというのは無駄。
なので、そういうことが起きにくくなるよう、コンポーネントは単一責任の原則を守りながら分解していくことが大事。
複数の子コンポーネントを持つ親コンポーネントにあるstateは、どこかの子コンポーネントに移譲できないか?とか考える。
Input
hr.icon
useMemo と useCallbackをいつ使用するか
まとめ
useMemoとuseCallbackは無思考に使うものではない。逆にコードを汚くして、最悪性能低下につながる可能性あり。
useMemoとuseCallbackは、子コンポーネントの再レンダリングを防ぐために使ったりする。
固定値だからといって無闇矢鱈に、useMemoやuseCallbackを使うものではない、逆に重くなったりする恐れあり。
React.memoに関しても、実際に使い場面はそんなにないはず。
いずれにせよ、無思考でいきなり性能チューニングをするのではなく、計測しながら適切に慎重にチューニングしなさい。
Reactのこともっとよくしろう! ~Reactパフォーマンス編~
性能をよくするという思考よりは「無駄な再レンダリングを防ぐ」という考え方で使うといいのかも。
注意.icon だけど!!!
やはり再レンダリング = メモ化って意味なんだが、、、
メモ化のコスト>再レンダリングのコストになってしまうと意味がない。
ここはマジで注意されたし。
なので、計測しながら調整されたし。
と思ったけど、devtoolはレンダリングにかかる時間は見れても、メモ化にかかる時間などは見れないので、そこまで意味ないかも
devtoolを使って計測するのがよろしいらしいです。
https://ja.reactjs.org/blog/2018/09/10/introducing-the-react-profiler.html
こいつの用途は、レンダリングの時間が長いボトルネックになってるDOMを探すこと